#include "UpdateChecker.h"
#include "tools.h"
#include "../Shared/shared.h"
#include <QtNetwork/QNetworkRequest>
#include <QMessageBox>
#include <QSettings>
#include <QFile>
#include <QDir>
#include <QCoreApplication>


UpdateChecker::UpdateChecker() : downloadXMLReply(NULL), downloadEXEReply(NULL), updateVersion(0)
{
	networkManager = new QNetworkAccessManager(this);
}

UpdateChecker::~UpdateChecker()
{
}

void UpdateChecker::beginToDownloadUpdateXML(const QString &updateXMLUrl)
{
	abortXMLReply();
	QNetworkRequest *request = new QNetworkRequest( QUrl(updateXMLUrl) );
	downloadXMLReply = networkManager->get(*request);
	delete request;

	connect( downloadXMLReply, SIGNAL(finished()), this, SLOT(endDownloadXML()) );
}

void UpdateChecker::beginToDownloadEXE(const QString &exeUrl)
{
	abortEXEReply();
	QNetworkRequest *request = new QNetworkRequest( QUrl(exeUrl) );
	downloadEXEReply = networkManager->get(*request);
	delete request;

	connect( downloadEXEReply, SIGNAL(finished()), this, SLOT(endDownloadEXE()) );
	connect( downloadEXEReply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(progressDownloadEXE(qint64, qint64)) );

	emit downloadEXEStarted();
}

void UpdateChecker::runApplication()
{
	QSettings ini(QSettings::IniFormat, QSettings::UserScope, COMPANY_NAME, PRODUCT_NAME);
	ini.remove("version");

	if ( !tools::runApplication(getApplicationPath()) )
	{
		QMessageBox::critical(NULL, "Downloader Launcher", "Application is not available");
	}

	QCoreApplication::exit();
}

bool UpdateChecker::getApplicationVersion(int &version)
{
	QSettings ini(QSettings::IniFormat, QSettings::UserScope, COMPANY_NAME, PRODUCT_NAME);
	QVariant versionVar = ini.value("version");
	if ( versionVar.isValid() )
	{
		version = versionVar.toInt();
		return true;
	}

	return false;
}

void UpdateChecker::endDownloadXML()
{
	bool needToRunApplication = true;

	if (downloadXMLReply->error() == QNetworkReply::NoError)
	{
		QByteArray response = downloadXMLReply->readAll();
		QString updateXml(response);
		if ( parseUpdateXml(updateXml, updateUrl, updateVersion) )
		{
			int appVersion;
			if (!getApplicationVersion(appVersion) || appVersion < updateVersion)
			{
				abortXMLReply();
				beginToDownloadEXE(updateUrl);
				needToRunApplication = false;
			}
		}
	}

	if (needToRunApplication)
		runApplication();
}

void UpdateChecker::endDownloadEXE()
{
	if (downloadEXEReply->error() == QNetworkReply::NoError)
	{
		QByteArray response = downloadEXEReply->readAll();
		QFile file( getApplicationPath() );
		if ( file.open(QIODevice::WriteOnly) )
		{
			file.write(response);
			file.close();
		}
	}

	emit downloadEXEFinished();

	runApplication();
}

void UpdateChecker::progressDownloadEXE(qint64 bytesReceived, qint64 bytesTotal)
{
	emit downloadEXEProgress(bytesReceived, bytesTotal);
}

void UpdateChecker::abortXMLReply()
{
	if (downloadXMLReply)
	{
		disconnect( downloadXMLReply, SIGNAL(finished()), this, SLOT(endDownloadXML()) );

		downloadXMLReply->deleteLater();
		downloadXMLReply = NULL;
	}
}

void UpdateChecker::abortEXEReply()
{
	if (downloadEXEReply)
	{
		disconnect( downloadEXEReply, SIGNAL(finished()), this, SLOT(endDownloadEXE()) );
		disconnect( downloadEXEReply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(progressDownloadEXE(qint64, qint64)) );

		downloadEXEReply->deleteLater();
		downloadEXEReply = NULL;
	}
}

bool UpdateChecker::parseUpdateXml(QString updateXmlText, QString &updateUrl, int &version)
{
	bool parsed = false;

	QString updateXmlBody = tools::extractXmlTagBody(updateXmlText, "update");
	if ( !updateXmlBody.isEmpty() )
	{
		QString versionText = tools::extractXmlTagBody(updateXmlText, "version");
		if ( !versionText.isEmpty() )
		{
			version = versionText.toInt();
			updateUrl = tools::extractXmlTagBody(updateXmlText, "win");
			if ( !updateUrl.isEmpty() )
				parsed = true;
		}
	}

	return parsed;
}

QString UpdateChecker::getApplicationPath()
{
	QSettings ini(QSettings::IniFormat, QSettings::UserScope, COMPANY_NAME, PRODUCT_NAME);
	QString appDirPath = QDir::toNativeSeparators( QFileInfo(ini.fileName()).absolutePath() );
	QDir dir;
	dir.mkdir(appDirPath);
	appDirPath += QDir::separator();

	return appDirPath + "downloader.exe";
}
